---
title: 终止长时间运行的任务
description: 并发程序通常会处理耗时较长的任务。所以需要某种方式在 Runnable / Callable 任务正常结束前提前终止它们，比如要关闭程序的时候。
---
并发程序通常会处理耗时较长的任务。所以需要某种方式在 Runnable / Callable 任务正常结束前提前终止它们，比如要关闭程序的时候。

Java最初的设计提供了某种机制（为了向后兼容，该机制仍然存在）来中断（interrupt）正在运行的任务，中断机制在阻塞方面存在一些问题。而且中断时机是不固定的。中断被认为是一种反模式，但由于向后兼容设计，我们仍然需要捕获 InterruptedException。

终止任务最好的方法是设置一个任务会定期检查的标识，由此任务可以通过自己的关闭流程来来优雅的终止。你可以请求任务在合适的时候自行终止，而不是在某一时刻突然终止。

但这么做还是有麻烦的地方————共享可变状态。如果该标识被其它任务操作，那么就有可能产生冲突。有很多解决该问题的方法，最常见的是通过 volatile 关键字。

但我们应该通过更简单的技术，避免 volatile 所带来的所有不确定性。

Java 5 引入了 Atomic 类，它提供了一组类型，让你可以无需担心并发问题，并放心使用。


```java title='下面引入了 AtomicBoolean 标识来告诉任务自行清理并退出：'
package pub.wkq.thread;

import java.util.concurrent.atomic.AtomicBoolean;

public class QuittableTask implements Runnable {
    private final int id;
    private final AtomicBoolean running = new AtomicBoolean(true);

    public void quit() {
        running.set(false);
    }

    public QuittableTask(int id) {
        this.id = id;
    }

    @Override
    public void run() {
        while (running.get()) {
            new Nap(0.1);
        }
        System.out.println(id + "  ");
    }
}
```
虽然多个任务可以成功在同一个实例中调用 quit() ,但 AtomicBoolean 阻止了多个任务同时修改 running，由此保证 quit() 方法是线程安全的。

running 需要定义为 AtomicBoolean 。

``` java
package pub.wkq.thread;

import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.IntStream;

public class QuittableTasks {
    public static final int COUNT = 1500;

    public static void main(String[] args) {
        ExecutorService exec = Executors.newCachedThreadPool();
        List<QuittableTask> tasks = IntStream.range(1, COUNT).mapToObj(QuittableTask::new).peek(quittableTask -> exec.execute(quittableTask)).toList();
        new Nap(1);
        tasks.forEach(QuittableTask::quit);
        exec.shutdown();
    }
}
```
在将任务收录到 List 中之前，通过 peek 将任务传递给 ExecutorService.

main() 保证只要还有任务在运行，程序就不会退出。任务关闭的顺序和创建的顺序并不一致，即使每个任务是按顺序执行 quit() 方法的，这些独立运行的任务对信号的响应是不可控的。

